Tipos Algebráicos Recursivos
Una primera definición:
data T = C1 t11 ... t1k1
...
| Cn tn1 ... tnkn
Donde T es el nuevo tipo a introducir y cada Ci es un constructor de dicho tipo, que toma ki parámetros.
Un elemento de tipo T se construye aplicando alguno de sus constructores Ci a argumentos con tipos acordes a los dados en la definición.
Los constructores se pueden ver como funciones de tipo:
C_i :: t_i1 -> ... -> t_ik_i -> T
El tipo que se está definiendo se puede usar como tipo componente en la definición.
Listas de caracteres:
data LChar = Nil | Cons Char LChar
Árboles Binarios de Enteros:
data Tree = Empty | Branch Int Tree Tree
Expresiones Numéricas:
data Expr = Lit Int | Add Expr Expr | Sub Expr Expr
Al igual que con las funciones, los constructores se pueden escribir de forma infija:
expr = (Lit 2) ‘Add‘ (Lit 4)
Pero también se pueden definir constructores infijos:
data Expr = Lit Int
| Expr :+: Expr
| Expr :-: Expr
-- los constructores tienen que empezar con ’:’.
Las definiciones pueden contener variables de tipos:
data T a1 ... am = C1 t11 ... t1k1
...
| Cn tn1 ... tnkn
Donde las variables ai
pueden ser usadas en la definición de los constructores.
data List a = Nil | Cons a (List a)
data Tree a = Empty | Branch a (Tree a) (Tree a)
Ejemplos de instancias:
Cons ’a’ (Cons ’b’ (Cons ’c’ Nil))
Tiene tipo List Char
Branch "tito" (Branch "toto" Empty Empty) Empty
Tiene tipo Tree String
Cons ’a’ (Cons True Nil)
Incorrecto
length :: List a -> Int
length Nil = 0
length (Cons xs) = 1 + length xs
maximum :: Ord a => List a -> Int
maximum (Cons x Nil) = x
maximum (Cons x xs) = max x (maximum xs)
sumOrd :: List Char -> Int
sumOrd Nil = 0
sumOrd (Cons x xs) = ord x + sumOrd xs
Se define solo para el caso feliz.
myDiv1 :: Int -> Int -> Int
myDiv1 x y | y /= 0 = div x y
myDiv2 :: Int -> Int -> Int
myDiv2 x y | y /= 0 = div x y
| otherwise = error "Division por cero"
myDiv3 :: Int -> Int -> Int
myDiv3 x y | y /= 0 = div x y
| otherwise = 0
myDiv4 :: Int -> Int -> Int -> Int
myDiv4 x y z | y /= 0 = div x y
| otherwise = z
Ver más: Tema 5 - Mónadas > Mónada Maybe
data Maybe a = Nothing | Just a
myDiv5 :: Int -> Int -> Maybe Int
myDiv5 x y | y /= 0 = Just (div x y)
| otherwise = Nothing
fromJust :: Maybe a -> a
fromMaybe :: a -> Maybe a -> a
maybe :: b -> (a -> b) -> Maybe a -> b
data Either a b = Left a | Right b
myDiv6 :: Int -> Int -> Either String Int
myDiv6 x y | y /= 0 = Right (div x y)
| otherwise = Left "Division por Cero"
-- Evaluar el tipo y obtener el valor
either :: (a -> c) -> (b -> c) -> Either a b -> c
-- Ejemplo: infinito en la div por 0
resDiv x y = either (const 9999) id (myDiv x y)
La sintaxis es la siguiente:
class NombreClase a where
f1 :: T1 -- funciones que se deben implementar
...
fn :: Tn
fi = ... -- funciones definidas por defecto
...
fj = ...
class Eq a where
(==),(/=) :: a → a → Bool -- funciones que se deben implementar
x /= y = not (x == y) -- funciones definidas por defecto
x == y = not (x /= y)
Las definiciones por defecto se pueden sobreescribir al definir una instancia.
En el caso de Eq, basta definir una operación para declarar que un tipo es miembro de la clase:
data ConIgual = Mismo | Mismisimo
instance Eq ConIgual where
Mismo == Mismo = True
Mismisimo == Mismisimo = True
_ == _ = False
Se pueden declarar clases que dependen de que sus tipos pertenezcan a otras clases.
class Eq a => Ord a where
(<),(<=),(>),(>=) :: a -> a -> Bool
max, min :: a -> a -> a
compare :: a -> a -> Ordering
data Ordering = LT | EQ | GT
Para que un tipo pertenezca a Ord debe proveer definiciones de las operaciones de Ord y de Eq.
Las instancias también pueden depender de que los tipos pertenezcan a otras clases:
instance (Eq a, Eq b) => Eq (a, b) where
(x, y) == (z,w) = x == z && y == w
Se permiten restricciones múltiples tanto en instancias, clases y funciones:
myPEq :: (Eq a, Eq b) => (a, b) -> (a, b) -> Bool
myPEq (x, y) (z,w) = (x, y) == (z,w)
Clase de los tipos Enumerados
class Ord a => Enum a where
succ, pred :: a -> a
toEnum :: Int -> a
fromEnum :: a -> Int
enumFrom :: a -> [a] -- [n .. ]
enumFromThen :: a -> a -> [a] -- [n, m .. ]
enumFromTo :: a -> a -> [a] -- [n .. m]
enumFromThenTo :: a -> a -> a -> [a] -- [n, n’ .. m]
[1 . . 10] ===> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[2, 4 . . 10] ===> [2, 4, 6, 8, 10]
take 10 [1 . .] ===> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[’a’ . . ’d’] ===> [’a’, ’b’, ’c’, ’d’]
Clase de los tipos Numéricos
class Num a where
(+),(-),(*) :: a -> a -> a
negate :: a -> a
abs :: a -> a
signum :: a -> a
fromInteger :: Integer -> a
Como mínimo hay que definir (+), (∗), abs, signum, fromInteger y negate o (−).
type ShowS = String -> String
class Show a where
showsPrec :: Int -> a -> ShowS
show :: a -> String
showList :: [a] -> ShowS
Como mínimo hay que definir show o showsPrec.
type ReadS a = String -> [(a, String)]
class Read a where
readsPrec :: Int -> ReadS a
readList :: ReadS [a]
Como mínimo hay que definir readsPrec.
La función read :: Read a => String -> a
está definida en top-level y utiliza readsPrec.
type ReadS a = String -> [(a, String)]
class Read a where
readsPrec :: Int -> ReadS a
readList :: ReadS [a]
class Eq a where
(==),(/=) :: a → a → Bool -- funciones que se deben implementar
x /= y = not (x == y) -- funciones definidas por defecto
x == y = not (x /= y)